#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmjpeg/djencode.h"
#include "dcmtk/dcmjpeg/djdecode.h"
#include "dcmtk/dcmjpeg/djrplol.h"
#include "dcmtk/dcmdata/dcmetinf.h"
#include "dcmtk/dcmdata/dcuid.h"

#include "image.h"


Image::Image()
{
    myDataset = nullptr;
}

Image::Image(
    std::shared_ptr<IDataSet>  aDataSet
)
{
    myDataset = aDataSet->GetDataSet();
}

Image::Image(
    const std::string& aDCMFile
)
{
    DcmFileFormat file;
    if (file.loadFile(OFFilename(aDCMFile)) != EC_Normal) {
        std::cout << "DicomFileFormat load file [" + aDCMFile + "] failed" << std::endl;
    }
    DcmDataset* dataset = file.getDataset();
    myDataset = new DcmDataset(*dataset);
}

Image::~Image()
{
    delete myDataset;
}

bool
Image::AddOverlay(
    const Overlay    aOverlay
)
{
    if (nullptr == myDataset) {
        std::cout << "myDataset is nullptr" << std::endl;
        return false;
    }

    if (myDataset->putAndInsertUint16(DCM_OverlayRows, aOverlay.myRows) != EC_Normal) {
        std::cout << "insert OverlayRows failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertUint16(DCM_OverlayColumns, aOverlay.myColumns) != EC_Normal) {
        std::cout << "insert OverlayColumns failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertOFStringArray(DCM_OverlayType, aOverlay.myType) != EC_Normal) {
        std::cout << "insert OverlayType failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertSint16(DCM_OverlayOrigin, aOverlay.myOrigin) != EC_Normal) {
        std::cout << "insert OverlayOrigin failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertUint16(DCM_OverlayBitsAllocated, 1) != EC_Normal) {
        std::cout << "insert OverlayBitsAllocated failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertUint16(DCM_OverlayBitPosition, 0) != EC_Normal) {
        std::cout << "insert OverlayBitPosition failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertUint8Array(DCM_OverlayData, aOverlay.myData, aOverlay.myRows * aOverlay.myColumns) != EC_Normal) {
        std::cout << "insert OverlayData failed" << std::endl;
        return false;
    }
    if (myDataset->putAndInsertOFStringArray(DCM_OverlayDescription, aOverlay.myDescription) != EC_Normal) {
        std::cout << "insert OverlayDescription failed" << std::endl;
        return false;
    }

    return true;
}

bool
Image::GeneralDCMFile(
    const std::string                 aFileName,
    const DCM_TransferSyntax          aXfer
)
{
    E_TransferSyntax xfer;
    if (DCM_TransferSyntax::EXS_LittleEndianExplicit == aXfer) {
        xfer = E_TransferSyntax::EXS_LittleEndianExplicit;
    } else if (DCM_TransferSyntax::EXS_LittleEndianImplicit == aXfer) {
        xfer = E_TransferSyntax::EXS_LittleEndianImplicit;
    }

    DcmFileFormat file(myDataset);
    if (file.saveFile(OFFilename(aFileName), xfer) != EC_Normal) {
        std::cout << "saveFile() failed" << std::endl;
        return false;
    }

    return true;
}

bool
Image::JPEGCompress(
    const std::string& aDCMFile,
    const std::string& aNewFileName,
    DCM_TransferSyntax aXfer
)
{
    DJEncoderRegistration::registerCodecs();

    DcmFileFormat fileformat;
    if (fileformat.loadFile(aDCMFile) != EC_Normal) {
        std::cout << "loadFile() failed, [" + aDCMFile + "]" << std::endl;
        return false;
    }

    DcmDataset* dataset = fileformat.getDataset();
    if (nullptr == dataset) {
        std::cout << "dataset is nullptr" << std::endl;
        return false;
    }
    E_TransferSyntax oXfer = dataset->getOriginalXfer();
    DcmXfer originalXfer(oXfer);
    if (originalXfer.isEncapsulated()) {
        std::cout << "isEncapsulated() return false, this file is already compressed" << std::endl;
        return false;
    }

    DcmMetaInfo* meta = fileformat.getMetaInfo();
    if (nullptr == meta) {
        std::cout << "meta is nullptr" << std::endl;
        return false;
    }
    std::string sop;
    if (meta->findAndGetOFString(DCM_MediaStorageSOPClassUID, sop) != EC_Normal) {
        std::cout << "find MediaStorageSOPClassUID failed" << std::endl;
        return false;
    }
    if (sop == UID_MediaStorageDirectoryStorage) {
        std::cout << "DICOMDIR file cannot be compressed" << std::endl;
        return false;
    }

    DJ_RPLossless params;
    E_TransferSyntax xfer;
    if (DCM_TransferSyntax::EXS_JPEGProcess14SV1 == aXfer) {
        xfer = E_TransferSyntax::EXS_JPEGProcess14SV1;
    }
    if (dataset->chooseRepresentation(xfer, &params) != EC_Normal) {
        std::cout << "chooseRepresentation() failed" << std::endl;
        return false;
    }
    if (!dataset->canWriteXfer(xfer)) {
        std::cout << "canWriteXfer() failed" << std::endl;
        return false;
    }
    if (fileformat.saveFile(OFFilename(aNewFileName), xfer) != EC_Normal) {
        std::cout << "saveFile() failed" << std::endl;
        return false;
    }

    DJEncoderRegistration::cleanup();

    return true;
}

bool
Image::JPEGDecompress(
    const std::string& aDCMFile,
    const std::string& aNewFileName,
    DCM_TransferSyntax aXfer
)
{
    DJDecoderRegistration::registerCodecs();

    DcmFileFormat fileformat;
    if (fileformat.loadFile(aDCMFile) != EC_Normal) {
        std::cout << "loadFile() failed, [" + aDCMFile + "]" << std::endl;
        return false;
    }

    DcmDataset* dataset = fileformat.getDataset();
    if (nullptr == dataset) {
        std::cout << "dataset is nullptr" << std::endl;
        return false;
    }

    DJ_RPLossless params;
    E_TransferSyntax xfer;
    if (DCM_TransferSyntax::EXS_LittleEndianExplicit == aXfer) {
        xfer = E_TransferSyntax::EXS_LittleEndianExplicit;
    } else if (DCM_TransferSyntax::EXS_LittleEndianImplicit == aXfer) {
        xfer = E_TransferSyntax::EXS_LittleEndianImplicit;
    }
    if (dataset->chooseRepresentation(xfer, &params) != EC_Normal) {
        std::cout << "chooseRepresentation() failed" << std::endl;
        return false;
    }
    if (!dataset->canWriteXfer(xfer)) {
        std::cout << "canWriteXfer() failed" << std::endl;
        return false;
    }
    if (fileformat.saveFile(OFFilename(aNewFileName), xfer) != EC_Normal) {
        std::cout << "saveFile() failed" << std::endl;
        return false;
    }

    DJDecoderRegistration::cleanup();

    return true;
}
